#ifndef GRAPHICSITEMFACTORY_H
#define GRAPHICSITEMFACTORY_H

#include <QList>
#include <QMap>
#include <QObject>
#include <QPainterPath>
#include <QString>

#include "LeIpc2581/Units.h"
#include "LeIpc2581/StandardPrimitive.h"

namespace Ipc2581b
{
    class Content;

    class Xform;

    class Color;
    class ColorRef;
    class ColorGroup;
    class DictionaryColor;

    class LineDesc;
    class LineDescRef;
    class LineDescGroup;
    class DictionaryLineDesc;

    class FillDesc;
    class FillDescRef;
    class FillDescGroup;
    class DictionaryFillDesc;

    class Polygon;
    class Butterfly;
    class Circle;
    class Contour;
    class Diamond;
    class Donut;
    class Ellipse;
    class Hexagon;
    class Moire;
    class Octagon;
    class Oval;
    class RectCenter;
    class RectCham;
    class RectCorner;
    class RectRound;
    class Thermal;
    class Triangle;
    class StandardPrimitive;
    class StandardPrimitiveRef;
    class StandardShape;
    class DictionaryStandard;

    class Arc;
    class Line;
    class Outline;
    class Polyline;
    class Simple;
    class Text;
    class UserSpecial;
    class UserPrimitive;
    class UserPrimitiveRef;
    class UserShape;
    class DictionaryUser;

    class Feature;

    class Pad;
    class Pin;
    class Fiducial;
    class Hole;
    class SlotCavity;
    class Target;
    class Marking;
    class Features;
    class PadstackHoleDef;
    class PadstackPadDef;
}

#include "LeGraphicsView/LeGraphicsItem.h" // For GraphicsFeature

enum GraphicsItemDataKey
{
    IpcDataKey = 0,
    IpcStdPrimitiveTypeKey,
    ItemFactoryTypeKey
};

Q_DECLARE_METATYPE(Ipc2581b::StandardPrimitive::StandardPrimitiveType)

enum class StatisticKey
{
    Color,
    ColorRef,
    Pen,
    PenRef,
    Brush,
    BrushRef,

    Butterfly,
    ButterflyRef,
    Circle,
    CircleRef,
    Contour,
    ContourRef,
    Diamond,
    DiamondRef,
    Donut,
    DonutRef,
    Ellipse,
    EllipseRef,
    Hexagon,
    HexagonRef,
    Moire,
    MoireRef,
    Octagon,
    OctagonRef,
    Oval,
    OvalRef,
    RectCenter,
    RectCenterRef,
    RectCham,
    RectChamRef,
    RectCorner,
    RectCornerRef,
    RectRound,
    RectRoundRef,
    Thermal,
    ThermalRef,
    Triangle,
    TriangleRef,
    StandardPrimitive,
    StandardPrimitiveRef,
    StandardShape,

    Arc,
    ArcRef,
    Line,
    LineRef,
    Outline,
    OutlineRef,
    Polyline,
    PolylineRef,
    Simple,
    SimpleRef,
    Text,
    TextRef,
    UserSpecial,
    UserSpecialRef,
    UserPrimitive,
    UserPrimitiveRef,
    UserShape,

    Feature,
    Features,

    Pad,
    Pin,
    Fiducial,
    Hole,
    Slot,
    Target,
    Marking,

    Transform
};

class GraphicsItemFactory: public QObject
{
    Q_OBJECT

public:
    explicit GraphicsItemFactory(QObject *parent = nullptr);
    ~GraphicsItemFactory();

    //GraphicsItem *createItem(const Ipc2581b::Outline *outline) const;
    //GraphicsItem *createItem(const Ipc2581b::Contour *contour) const;

    QList<LeGraphicsItem *> createItems(const Ipc2581b::Features *features) const;
    LeGraphicsItem *createItem(const Ipc2581b::Pad *pad) const;
    LeGraphicsItem *createItem(const Ipc2581b::Pin *pin) const;
    LeGraphicsItem *createItem(const Ipc2581b::Fiducial *fiducial) const;
    LeGraphicsItem *createItem(const Ipc2581b::Hole *hole) const;
    LeGraphicsItem *createItem(const Ipc2581b::SlotCavity *slotCavity) const;
    LeGraphicsItem *createItem(const Ipc2581b::Target *target) const;
    LeGraphicsItem *createItem(const Ipc2581b::Marking *marking) const;
    LeGraphicsItem *createItem(const Ipc2581b::PadstackHoleDef *hole) const;
    LeGraphicsItem *createItem(const Ipc2581b::PadstackPadDef *pad) const;

    // TODO: PadstackHole, PadstackPad

    // TBD: For component highlight: Xfrom, Position, Outline
    // TBD:



    void loadDictionaries(const Ipc2581b::Content *content);

    void loadColorDictionary(const Ipc2581b::DictionaryColor *dict);
    void loadPenDictionary(const Ipc2581b::DictionaryLineDesc *dict);
    void loadBrushDictionary(const Ipc2581b::DictionaryFillDesc *dict);
    void loadStandardPrimitiveDictionary(const Ipc2581b::DictionaryStandard *dict);
    void loadUserPrimitiveDictionary(const Ipc2581b::DictionaryUser *dict);

    QStringList colorNames() const;
    QColor createColor(const QString &name) const;
    QColor createColor(const Ipc2581b::Color *color) const;
    QColor createColor(const Ipc2581b::ColorRef *colorRef) const;
    QColor createColor(const Ipc2581b::ColorGroup *colorGroup) const;

    QStringList penNames() const;
    QPen createPen(const QString &name) const;
    QPen createPen(const Ipc2581b::LineDesc *lineDesc) const;
    QPen createPen(const Ipc2581b::LineDescRef *lineDescRef) const;
    QPen createPen(const Ipc2581b::LineDescGroup *lineDescGroup) const;

    QStringList brushNames() const;
    QBrush createBrush(const QString &name) const;
    QBrush createBrush(const Ipc2581b::FillDesc *fillDesc) const;
    QBrush createBrush(const Ipc2581b::FillDescRef *fillDescRef) const;
    QBrush createBrush(const Ipc2581b::FillDescGroup *fillDescGroup) const;

    QTransform createTransform(Ipc2581b::Xform *xform) const;

    LeGraphicsItem *createItem(const Ipc2581b::Feature *feature, LeGraphicsFeature featureType) const;

    QStringList standardPrimitiveNames() const;
    LeGraphicsItem *createStandardPrimitive(const QString &name, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createStandardPrimitive(const Ipc2581b::StandardPrimitive *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createStandardPrimitive(const Ipc2581b::StandardPrimitiveRef *ref, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createStandardPrimitive(const Ipc2581b::StandardShape *shape, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Butterfly *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Circle *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Contour *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Diamond *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Donut *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Ellipse *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Hexagon *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Moire *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Octagon *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Oval *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::RectCenter *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::RectCham *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::RectCorner *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::RectRound *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Thermal *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Triangle *primitive, LeGraphicsFeature featureType) const;

    QStringList userPrimitiveNames() const;
    LeGraphicsItem *createUserPrimitive(const QString &name, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createUserPrimitive(const Ipc2581b::UserPrimitive *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createUserPrimitive(const Ipc2581b::UserPrimitiveRef *ref, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createUserPrimitive(const Ipc2581b::UserShape *shape, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Arc *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Line *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Outline *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Polyline *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Simple *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::Text *primitive, LeGraphicsFeature featureType) const;
    LeGraphicsItem *createItem(const Ipc2581b::UserSpecial *primitive, LeGraphicsFeature featureType) const;

    qreal scaleFactor() const;
    QString unit() const;

    void clearDictionaries();

    void clearStats();
    void printStats();

    qreal mapFromIpc(qreal value) const
    {
        return value * m_ipcScaleFactor;
    }
    QPointF mapFromIpc(const QPointF &point) const
    {
        return QPointF(point.x() * m_ipcScaleFactor, point.y() * m_ipcScaleFactor);
    }
    QPointF mapFromIpc(qreal x, qreal y) const
    {
        return QPointF(x * m_ipcScaleFactor, y * m_ipcScaleFactor);
    }

private:
    void setIpcUnit(Ipc2581b::Units unit);
    static QPolygonF createRegularPolygon(int sideCount, qreal radius, qreal startAngle = 0);
    static QPolygonF createHexagon(qreal radius);
    static QPolygonF createOctagon(qreal radius);
    QPainterPath &addPolygonToPath(const Ipc2581b::Polygon *polygon, QPainterPath &path) const;
    QPainterPath &addPolylineToPath(const Ipc2581b::Polyline *polygon, QPainterPath &path) const;
    QPainterPath strokePath(const QPainterPath &path, const Ipc2581b::LineDescGroup *lineDescGroup) const;
    template <class T> QPainterPath &addPolyToPath(const T *poly, QPainterPath &path) const;

    Ipc2581b::Units m_ipcUnit;
    qreal m_ipcScaleFactor;
    QHash<QString, QColor> m_colorDictionary;
    QHash<QString, QPen> m_penDictionary;
    QHash<QString, QBrush> m_brushDictionary;
    QHash<QString, LeGraphicsItem *> m_stdPrimitiveDictionary;
    QHash<QString, LeGraphicsItem *> m_userPrimitiveDictionary;

    mutable QMap<StatisticKey, int> m_statCounter;
};

#endif // GRAPHICSITEMFACTORY_H
